/* test_threads_max_usage():
	This test creates 'UTHREAD_MAX_THREADS' threads, and each tread uses almost
	UTHREAD_DEF_STACK_SIZE of its stack.

	WARNNING: This test did not pass for us when using a big part of the stack
	(in each thread). So now it's set to not use too much. You can modify
	'SIZE_TO_HOLD_ON_EACH_STACK' as you wish.

	This test does the following.
	Creates 255 additional threads. Each thread takes almost
	UTHREAD_DEF_STACK_SIZE memory on its stack.
	We iterated over all the threads in cyclic order, 3 times, and each
	thread does:
		* adds its (id+1) to the global variable global_sum.
		* yeilds.
	Each thread does that when it holds the lock 'global_lock'.

*/


#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#include "test_general.h"

#include "user_threads.h"
#include "user_mutex.h"

uthread_mutex_t global_mutex;
long global_sum;


bool test_threads_max_usage();

#define THREADS_NUM NR_THREADS
//#define SIZE_TO_HOLD_ON_EACH_STACK (UTHREAD_DEF_STACK_SIZE-500)
//#define SIZE_TO_HOLD_ON_EACH_STACK 2000 // This cause problmes
#define SIZE_TO_HOLD_ON_EACH_STACK 1000 //TODO: why can't it be way bigger?

static int thread_start_routine(void * param) {
	/* param = the id of the thread */
	TEST_ASSERT (uthread_self() == (thread_id)param);

	int i;

	char array[SIZE_TO_HOLD_ON_EACH_STACK];
	for (i=0; i<SIZE_TO_HOLD_ON_EACH_STACK; i++) {
		array[i] = 0;
	}


	for (i=0;i<3;i++) {
		int tmp = uthread_mutex_lock(global_mutex);
		TEST_ASSERT(MUTEX_SUCCESS == tmp);

		global_sum += (uthread_self()+1);

		tmp = uthread_mutex_unlock(global_mutex);
		TEST_ASSERT(MUTEX_SUCCESS == tmp);

		uthread_yield();
	}

	if (uthread_self() > 0) {
		thread_ok[uthread_self()] = true;
	}
	return 0;
}


bool test_threads_max_usage()
{
	init_individual_test();

	int tmp;

	if (initilize_uthread_in_each_test) {
		tmp = uthread_init();
		TEST_ASSERT(tmp == UTHREAD_SUCCESS);
	}

	thread_id threads[THREADS_NUM];

	threads[0] = uthread_self();

	tmp = uthread_mutex_init(&global_mutex);
	TEST_ASSERT(MUTEX_SUCCESS == tmp);

	tmp = uthread_mutex_lock(global_mutex);
	TEST_ASSERT(MUTEX_SUCCESS == tmp);

	global_sum = 0;

	int i;
	for (i=1; i<THREADS_NUM; i++) {
		threads[i] = uthread_create(thread_start_routine, (void*)i);
		TEST_ASSERT(threads[i] == i);
	}

	tmp = uthread_mutex_unlock(global_mutex);
	TEST_ASSERT(MUTEX_SUCCESS == tmp);

	thread_start_routine((void*)0);


	for (i=1; i<THREADS_NUM; i++) {
		tmp = uthread_join(threads[i],NULL);
		TEST_ASSERT(UTHREAD_SUCCESS == tmp);
	}

	tmp = uthread_mutex_destroy(global_mutex);
	TEST_ASSERT(MUTEX_SUCCESS == tmp);


	// 1+2+3+...+256 = 32896
	TEST_ASSERT(3*32896 == global_sum);

	thread_ok[0] = true;
	TEST_ASSERT(are_all_threads_ok(THREADS_NUM));
	return true;
}
